#include <iostream>
#include <string>
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>

class DictClient
{
public:
    DictClient(const std::string &sip, int sport): 
        _baseloop(_loopthread.startLoop()),
        _client(_baseloop, muduo::net::InetAddress(sip, sport), "DictClient"),
        _downlatch(1)
    {
        // 设置连接事件回调（给_server, _server会设置给_baseloop）
        _client.setConnectionCallback(std::bind(&DictClient::onConnection, this, std::placeholders::_1));

        // 设置消息事件回调（给_server, _server会设置给_baseloop）
        _client.setMessageCallback(std::bind(&DictClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 连接服务器
        // connect不会等服务器那边建立好连接，客户端这边连接建立好之后就往下走，所以有可能服务器没有准备好，客户端就已经开始发送数据了
        // 所以connect之后需要调用wait阻塞，然后等待被激活
        _client.connect();
        _downlatch.wait(); // 当计数为0时，被激活

        // 开始事件循环
        // 注意：这里不能直接调用loop，因为这是一个死循环，就会导致主线程一直阻塞
        // 应该创建一个线程来完成事件循环，当事件到来的时候直接调用回调来处理
        // 但是muduo给我们提供了一个类：muduo::net::EventLoopThread，里面封装了线程和EventLoop
        // 当我们执行startLoop的时候就自动开始事件循环了，同时这个函数返回内部EventLoop的指针
        // _baseloop->loop(); // EventLoopThread::startLoop()内部已经调用，无需再调用
    }

    void send(const std::string &msg)
    {
        if (_conn->connected() == false)
        {
            std::cout << "not connected\n";
            return;
        }
        _conn->send(msg);
    }

private:
    void onConnection(const muduo::net::TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            std::cout << "connected\n";
            _conn = conn;
            _downlatch.countDown(); // 当连接建立完成之后，计数减减，当为0时，激活wait
        }
        else
        {
            std::cout << "disconnected\n";
            _conn.reset();
        }
    }

    void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp time)
    {
        std::string msg = buf->retrieveAllAsString();
        std::cout << msg << std::endl;
    }

private:
    // 注意成员属性的声明顺序
    muduo::net::EventLoopThread _loopthread;
    muduo::net::EventLoop* _baseloop;
    muduo::net::TcpClient _client;
    muduo::CountDownLatch _downlatch;
    muduo::net::TcpConnectionPtr _conn;
};

int main()
{
    DictClient dc("127.0.0.1", 8888);
    while (1)
    {
        std::string msg;
        std::cin >> msg;
        dc.send(msg);
    }
}
